package com.siecom.framework.channel;

import android.util.Log;
import android.app.Instrumentation;
import android.os.Build;
import android.os.RemoteException;
import android.view.KeyEvent;

import com.siecom.tools.ByteUtil;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.InvalidParameterException;

import android_serialport_api.SerialPort;

/**
 * 
 *
 */
public class SerialHelper{
	private  Mutex mutex = new Mutex();
	private SerialPort mSerialPort;
	private OutputStream mOutputStream;
	private InputStream mInputStream;
	private ReadThread mReadThread;
	private SendThread mSendThread;
	private boolean keyMode = false;
	private int threadRunFlag = 0;
	//private String sPort="/dev/ttySAC1";
	private String sPort="/dev/ttyHSL1";//ttyHSL0 for hardware v1, ttyHSL1 for hardware v2
	private int iBaudRate=115200;
	private boolean _isOpen=false;
	private byte[] _bLoopData=new byte[]{0x30};
	private int iDelay=500;

	public byte[] FifoBuffer=new byte[1024*100];
	public int ofst_free=0,ofst_last=0;

	
	//----------------------------------------------------
	public SerialHelper(String sPort,int iBaudRate){
		if(Build.MODEL.equals("US804")) this.sPort = "/dev/ttyHSL2";
		else this.sPort = sPort;
		this.iBaudRate=iBaudRate;
		
		Log.e("Scan","Port:"+sPort+" BaudRate:"+iBaudRate);
	}
	public SerialHelper(){
		//this("/dev/ttySAC1",115200);
		this("/dev/ttyHSL1",115200);////ttyHSL0 for hardware v1, ttyHSL1 for hardware v2
	} 
	public SerialHelper(String sPort){
		this(sPort,115200);
	}
	public SerialHelper(int iBaudRate){
		this("/dev/ttyHSL1",iBaudRate);
	}
	public SerialHelper(String sPort,String sBaudRate){
		this(sPort,Integer.parseInt(sBaudRate));
	}
	
	//----------------------------------------------------
	public void open() throws SecurityException, IOException,InvalidParameterException{
		
		if(this.sPort.equals("/dev/ttyHSL1")==true){
			Log.d("SerialHelper", "before lock ");
			mutex.lock();
			Log.d("SerialHelper", "after lock");
		}
		if(this.sPort.equals("/dev/ttyS3")==true){
			Log.d("SerialHelper", "lock ");
			mutex.lock();
			
		}

		if(this.sPort.equals("/dev/ttyHSL1")==false&&_isOpen==false){
			if(mSerialPort==null){
				mSerialPort =  new SerialPort(new File(sPort), iBaudRate, 0);
				mOutputStream = mSerialPort.getOutputStream();
				mInputStream = mSerialPort.getInputStream();
				mReadThread = new ReadThread();
				mReadThread.start();
			}
		}
		if(Build.MODEL.equals("D2000")||Build.MODEL.equals("PDA")||Build.MODEL.equals("JA-C5")){
			if(this.sPort.equals("/dev/ttyHSL1")&&threadRunFlag==0){
				mSerialPort =  new SerialPort(new File(sPort), iBaudRate, 0);
				mOutputStream = mSerialPort.getOutputStream();
				mInputStream = mSerialPort.getInputStream();
				mReadThread = new ReadThread();
				mReadThread.start();
			}
		}
		else{
			if(this.sPort.equals("/dev/ttyHSL1")&&_isOpen==false){
				mSerialPort =  new SerialPort(new File(sPort), iBaudRate, 0);
				mOutputStream = mSerialPort.getOutputStream();
				mInputStream = mSerialPort.getInputStream();
				mReadThread = new ReadThread();
				mReadThread.start();
			}
		}
		
		threadRunFlag = 1;
		//mSendThread = new SendThread();
		//mSendThread.setSuspendFlag();
		//mSendThread.start();
		_isOpen=true;

	}
	public void PortClose(){
		Log.d("PortClose","PortClose");
		if (mReadThread != null)
			mReadThread.interrupt();
	
		if (mSerialPort != null) {
			mSerialPort.close();
			mSerialPort = null;
		}
		if(this.sPort.equals("/dev/ttyHSL1")==true) mutex.unlock();
		if(this.sPort.equals("/dev/ttyS3")==true) {
			Log.d("SerialHelper", "unlock");
			mutex.unlock();
			
		}
		_isOpen=false;
		threadRunFlag = 0;
	}
	public void createRecvThread()
	{
		mReadThread = new ReadThread();
		mReadThread.start();
	}
	//----------------------------------------------------
	public void close(){
		//Log.d("log","close");
		if(this.sPort.equals("/dev/ttyHSL1")==false||(Build.MODEL.equals("D2000")==false&&Build.MODEL.equals("PDA")==false&&Build.MODEL.equals("JA-C5")==false)){
			if (mReadThread != null)
				mReadThread.interrupt();
		
			if (mSerialPort != null) {
				mSerialPort.close();
				mSerialPort = null;
			}
		}
		else {
			threadRunFlag = 2;
		}
		_isOpen=false;
		if(this.sPort.equals("/dev/ttyHSL1")==true) mutex.unlock();
		if(this.sPort.equals("/dev/ttyS3")==true) {
			Log.d("SerialHelper", "unlock");
			mutex.unlock();
			
		}
		setBaudRate(115200);
	}
	public void SetMode(boolean mode)
	{
		keyMode = mode;
	}
	public int reset()
	{
		//return mSerialPort.reset();
		ofst_free=0;
		ofst_last=0;
		return 0;
	}
	
	public void Push(byte[] bInArray,int length){
		
		//if(keyMode==false||(this.sPort!="/dev/ttyHSL0"||this.iBaudRate!=9600))////ttyHSL0 for hardware v1, ttyHSL1 for hardware v2
		if((keyMode==false||(this.sPort.equals("/dev/ttyHSL0")==false||this.iBaudRate!=9600))&&threadRunFlag==1)////ttyHSL0 for hardware v1, ttyHSL1 for hardware v2
		{
			if(ofst_free+length<FifoBuffer.length) {
				System.arraycopy(bInArray, 0, FifoBuffer, ofst_free, length);
				ofst_free = ofst_free + length;
			}
			else {
				System.arraycopy(bInArray, 0, FifoBuffer, ofst_free, FifoBuffer.length-ofst_free);
				System.arraycopy(bInArray, FifoBuffer.length - ofst_free, FifoBuffer, 0, length + ofst_free - FifoBuffer.length);
				ofst_free = length+ofst_free-FifoBuffer.length;
			}
		}
		else
		{
			int keycode = 0;
			for(int i=0;i<length;i++){
				keycode = 0;
				Log.d("SerialHelper", "Push: "+bInArray[i]);
				try { 
					switch(bInArray[i]){
					case 0x14://0
						keycode = KeyEvent.KEYCODE_0;
						break;
					case 1:
						keycode = KeyEvent.KEYCODE_1;
						break;
					case 2:
						keycode = KeyEvent.KEYCODE_2;
						break;
					case 3:
						keycode = KeyEvent.KEYCODE_3;
						break;
					case 4:
						keycode = KeyEvent.KEYCODE_4;
						break;
					case 5:
						keycode = KeyEvent.KEYCODE_5;
						break;
					case 6:
						keycode = KeyEvent.KEYCODE_6;
						break;
					case 7:
						keycode = KeyEvent.KEYCODE_7;
						break;
					case 8:
						keycode = KeyEvent.KEYCODE_8;
						break;
					case 9://9
						keycode = KeyEvent.KEYCODE_9;
						break;
					case 0x11://backspace
						keycode = KeyEvent.KEYCODE_DEL;
						break;
					case 0x12://cancel
						keycode = KeyEvent.KEYCODE_BACK;
						break;
					case 0x13://enter
						keycode = KeyEvent.KEYCODE_ENTER;
						break;
					case 0x15://enter
						keycode = KeyEvent.KEYCODE_STAR;
						break;
					case 0x16://enter
						keycode = KeyEvent.KEYCODE_POUND;
						break;
					}
					if(keycode!=0){
			         Instrumentation inst=new Instrumentation(); 
			         inst.sendKeyDownUpSync(keycode);
					}
			        keycode = 0;
			    } catch(Exception e) { 
			        e.printStackTrace();
			    } 	
			}
		}
	}
	public int Pop(byte[] bOutArray,int timeout){
		for(int i=0;i<timeout;i+=10){
			if(ofst_free!=ofst_last){
				bOutArray[0]=FifoBuffer[ofst_last++];
				//Log.d("SerialHelper", "Pop: "+ByteUtil.arrayCopy2Hex(bOutArray,0,1));
				ofst_last %= FifoBuffer.length;
				return 0;
			}
			try
			{
				Thread.sleep(10);
			} catch (InterruptedException e)
			{
				e.printStackTrace();
			}
		}
		return 1;
	}
	public boolean CheckUart(){
		if(ofst_free!=ofst_last)
			return true;
		else return false;
	}

	public void Crc16CCITT(byte[] bData,int length,byte[] crcOut)
	{
		if (mSerialPort != null) {
			mSerialPort.Crc16CCITT(bData, length, crcOut);
		}
	}
	
	public void QuickCrc32(byte[] bData,int length,byte[] crcOut)
	{
		if (mSerialPort != null) {
			mSerialPort.Quick32crc(bData, length, crcOut);
		}
	}
	//----------------------------------------------------
	public void send(byte[] bOutArray){
		try{
			//mSerialPort.reset();			
//			Log.e("SerialHelper","send:"+ ByteUtil.toHexString(bOutArray,bOutArray.length));
			mOutputStream.write(bOutArray);
		} catch (IOException e)
		{
	        if(mutex!=null) mutex.unlock();
			e.printStackTrace();
		}
	}
	
	//----------------------------------------------------
	private class ReadThread extends Thread {
		@Override
		public void run() {
			super.run();
			while(!isInterrupted()) {
				try
				{

					if (mInputStream == null) return;
					//Log.d("serialhelper","begin recv:");					
					int len;
					if(sPort.equals("/dev/smd8")) len = 64;
					else len = mInputStream.available();

					if(len > 0)
					{
						byte[] buffer = new byte[len];
						int size = mInputStream.read(buffer);
						if (size > 0){
							Push(buffer, size);
							Log.e("SerialHelper", "recv:" + ByteUtil.toHexString(buffer, size));
						}
					}
					else {
						Thread.sleep(100);
					}
				} catch (Throwable e)
				{
			        if(mutex!=null) mutex.unlock();
					e.printStackTrace();
					return;
				}
			}
		}
	}
	//----------------------------------------------------
	private class SendThread extends Thread{
		public boolean suspendFlag = true;// éæ¤æ·éæ¤æ·éç«­ç¨ç¢æ·æ§éæ¤æ?
		@Override
		public void run() {
			super.run();
			while(!isInterrupted()) {
				synchronized (this)
				{
					while (suspendFlag)
					{
						try
						{
							wait();
						} catch (InterruptedException e)
						{
					        if(mutex!=null) mutex.unlock();
							e.printStackTrace();
						}
					}
				}
				send(getbLoopData());
				try
				{
					Thread.sleep(iDelay);
				} catch (InterruptedException e)
				{
					e.printStackTrace();
				}
			}
		}

		//éç«­ç­¹æ·éæ¤æ·å
		public void setSuspendFlag() {
		this.suspendFlag = true;
		}
		
		//éæ¤æ·éæ¤æ·éç«­ç­¹æ·
		public synchronized void setResume() {
		this.suspendFlag = false;
		notify();
		}
	}
	//----------------------------------------------------
	public int getBaudRate()
	{
		return iBaudRate;
	}
	public boolean setBaudRate(int iBaud)
	{
		//Log.d("setBaudRate","Port��"+sPort);
		//Log.d("setBaudRate","setBaudRate��"+iBaud);
		if (_isOpen)
		{
			return false;
		} else
		{
			iBaudRate = iBaud;
			return true;
		}
	}
	public boolean setBaudRate(String sBaud) 
	{
		int iBaud = Integer.parseInt(sBaud);
		return setBaudRate(iBaud);
	}
	//----------------------------------------------------
	public String getPort()
	{
		return sPort;
	}
	public boolean setPort(String sPort)
	{
		if (_isOpen)
		{
			return false;
		} else
		{
			this.sPort = sPort;
			return true;
		}
	}
	//----------------------------------------------------
	public boolean isOpen()
	{
		return _isOpen;
	}
	//----------------------------------------------------
	public byte[] getbLoopData()
	{
		return _bLoopData;
	}
	//----------------------------------------------------
	public void setbLoopData(byte[] bLoopData)
	{
		this._bLoopData = bLoopData;
	}
	//----------------------------------------------------
	public int getiDelay()
	{
		return iDelay;
	}
	//----------------------------------------------------
	public void setiDelay(int iDelay)
	{
		this.iDelay = iDelay;
	}
	//----------------------------------------------------
	public void startSend()
	{
		if (mSendThread != null)
		{
			mSendThread.setResume();
		}
	}
	//----------------------------------------------------
	public void stopSend()
	{
		if (mSendThread != null)
		{
			mSendThread.setSuspendFlag();
		}
	}
	public void sendMcuCommand(String command){
		CharSequence t = command;
		int i;

		Log.d("SerialHelper", " " + t + " , length  " + t.length());
		char[] text = new char[t.length()];

		for (i = 0; i < t.length(); i++) {
			text[i] = t.charAt(i);
		}
		try {
			mOutputStream.write(new String(text).getBytes());
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	//----------------------------------------------------
	//protected abstract void onDataReceived(ComBean ComRecData);
	public int available() {
		// TODO Auto-generated method stub
		return (FifoBuffer.length + ofst_free - ofst_last) % FifoBuffer.length;
	}

	public int write(byte[] reqMsg, int reqMsgLen) {
		// TODO Auto-generated method stub
		byte[] sendbuffer= new byte[reqMsgLen];
		System.arraycopy(reqMsg, 0, sendbuffer, 0, reqMsgLen);
		send(sendbuffer);
		return 0;

	}

	public int read(byte[] rspMsg, int reveiveLen, int wantedLen) {
		// TODO Auto-generated method stub
		byte[] ch = new byte[1];
		if (ofst_free != ofst_last) {
			Pop(ch,100);
		}
		rspMsg[reveiveLen]=ch[0];
		return 1;
	}
	public int read(byte[] rspMsg, int wantedLen) {
		// TODO Auto-generated method stub
		byte[] ch = new byte[1];
		int i;
		for(i=0;i<wantedLen;i++) {
			if (ofst_free != ofst_last) {
				Pop(ch, 100);
			}
			rspMsg[i] = ch[0];
		}
		return i;
	}
}
